package pl.cba.solgar.game.rogal;

import java.util.Vector;

import org.newdawn.slick.AngelCodeFont;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.Font;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

import pl.cba.solgar.game.rogal.Timeline.TimelineEntity;
import pl.cba.solgar.game.rogal.commands.Command;
import pl.cba.solgar.game.rogal.commands.MessagesHistoryCommand;
import pl.cba.solgar.game.rogal.commands.PrintCommandsCommand;
import pl.cba.solgar.game.rogal.commands.QuitCommand;
import pl.cba.solgar.game.rogal.commands.debug.GenerateLevelCommand;
import pl.cba.solgar.game.rogal.commands.debug.GeneratorConnectCommand;
import pl.cba.solgar.game.rogal.commands.debug.GeneratorDigCommand;
import pl.cba.solgar.game.rogal.commands.debug.GeneratorPostProcessCommand;
import pl.cba.solgar.game.rogal.commands.debug.GeneratorStepCommand;
import pl.cba.solgar.game.rogal.commands.debug.GoEastCommand;
import pl.cba.solgar.game.rogal.commands.debug.GoNorthCommand;
import pl.cba.solgar.game.rogal.commands.debug.GoNorthEastCommand;
import pl.cba.solgar.game.rogal.commands.debug.GoNorthWestCommand;
import pl.cba.solgar.game.rogal.commands.debug.GoSouthCommand;
import pl.cba.solgar.game.rogal.commands.debug.GoSouthEastCommand;
import pl.cba.solgar.game.rogal.commands.debug.GoSouthWestCommand;
import pl.cba.solgar.game.rogal.commands.debug.GoWestCommand;
import pl.cba.solgar.game.rogal.commands.debug.ResetMapBgCommand;
import pl.cba.solgar.game.rogal.commands.debug.TurnCmd;
import pl.cba.solgar.game.rogal.commands.debug.WaterDebugCommand;
import pl.cba.solgar.game.rogal.console.Console;
import pl.cba.solgar.game.rogal.generators.BSPGenerator;
import pl.cba.solgar.game.rogal.map.Map;
import playground.astar.AStarDataProvider;
import playground.astar.AStarNode;

public class Rogal extends BasicGame implements AStarDataProvider
{

    public static Rogal rogal = null;
    public static AppGameContainer app;
    public Map map;
    public AStarNode[][] astarNodes;
    public final static int MESSAGE_CONSOLE_SIZE = 3;
    public final static int TILE_SIZE = 32;
    public final static int FONT_WIDTH = 8;
    public final static int FONT_HEIGHT = 14;
    public final static int CONSOLE_ROWS = 34;
    public final static int CONSOLE_COLS = 80;
    public final static int MAP_WIDTH = CONSOLE_COLS;
    public final static int MAP_HEIGHT = CONSOLE_ROWS - 2 - MESSAGE_CONSOLE_SIZE;
    public static Font consoleFont;
    private Console console;
    public final static int TRANSLATE_Y = 0;//(480 - 350) / 2;

    public MessageConsole msgConsole;
    public CommandRecognizer cmdRecognizer;
    public Timeline timeline;
    public TimelineEntity playerTimelineEntity;

    private Vector<Printable> mapPrintables;
    private Vector<Printable> consolePrintables;

    public BSPGenerator generator;
    boolean debug = false;

    public static void main(String[] args)
    {
        System.out.println("Rogal started...");

        try
        {
            app = new AppGameContainer(new Rogal("Rogal 0.0.2"));
            //app.setDisplayMode(CONSOLE_COLS * FONT_WIDTH, CONSOLE_ROWS * FONT_HEIGHT, true);
            app.setDisplayMode(640, 480, false);
            app.setMouseGrabbed(false);
            app.setShowFPS(false);
            app.start();
        }
        catch (SlickException e)
        {
            e.printStackTrace();
        }
    }

    public Rogal(String title)
    {
        super(title);
    }
    public int mover_x = 0;
    public int mover_y = 0;

    public void render(GameContainer container, Graphics g) throws SlickException
    {
    	console.resetTranslate();
        g.setColor(Color.white);

        Input input = app.getInput();
        int mx = input.getMouseX();
        int my = input.getMouseY();
        g.setColor(Color.cyan);
        g.fillRect(mx, my, 8, 8);

        g.translate(0, TRANSLATE_Y); // for full screen to center view port in height

        g.setFont(consoleFont);
        console.setTranslateY(MESSAGE_CONSOLE_SIZE);
        map.render(console);
        console.setChar('@', mover_x, mover_y, Color.blue);
        for (Printable p : mapPrintables)
			p.print(console);
        console.resetTranslate();

        msgConsole.render(console);
        console.setColor(Color.white);

        for (int y = CONSOLE_ROWS - 2;  y < CONSOLE_ROWS; y++)
        	for (int x = 0; x < CONSOLE_COLS; x++)
        		console.print(" ", x, y, Color.black, null);

        console.print(" > " + cmdRecognizer.getChars(), 0, CONSOLE_ROWS - 2);

        for (Printable p : consolePrintables)
			p.print(console);

        console.render(g);

        if (debug && generator != null)
            generator.paintDebug(g);

        if (selectedRoom != null)
        {
            g.setColor(Color.blue);
            g.fillRect((selectedRoom.x + selectedRoom.w / 2) * Rogal.FONT_WIDTH - 8, (selectedRoom.y + selectedRoom.h / 2) * Rogal.FONT_HEIGHT - 8, 16, 16);
        }

        for (Room room : Map._rooms)
            room.debugPaint(g);

        //console.clear();
    }

    public boolean registerMapPrintable(Printable p) {
    	if (mapPrintables.contains(p))
    		return false;
    	else
    		mapPrintables.add(p);
    	return true;
    }

    public boolean registerConsolePrintable(Printable p) {
    	if (consolePrintables.contains(p))
    		return false;
    	else
    		consolePrintables.add(p);
    	return true;
    }

    public void unregisterMapPrintable(Printable p) {
		mapPrintables.removeElement(p);
    }

    public void unregisterConsolePrintable(Printable p) {
    	consolePrintables.removeElement(p);
    }

    public void init(GameContainer container) throws SlickException
    {
        console = new Console(CONSOLE_COLS, CONSOLE_ROWS, FONT_HEIGHT, FONT_WIDTH);
        consoleFont = new AngelCodeFont("zzz.fnt", "zzz_0.png");

        map = new Map(MAP_WIDTH, MAP_HEIGHT);
        astarNodes = new AStarNode[MAP_WIDTH][MAP_HEIGHT];

        for (int y = 0; y < MAP_HEIGHT; y++)
        	for (int x = 0; x < MAP_WIDTH; x++)
        		astarNodes[x][y] = new AStarNode(x, y);

        app.getInput().disableKeyRepeat();
        msgConsole = MessageConsole.createInstance(0, 0, CONSOLE_COLS, MESSAGE_CONSOLE_SIZE);

        mapPrintables = new Vector<Printable>();
        consolePrintables = new Vector<Printable>();

        timeline = new Timeline();
        playerTimelineEntity = timeline.new TimelineEntity(timeline, 128);

        // init commands
        // test commands
        cmdRecognizer = new CommandRecognizer();
        cmdRecognizer.addCommand(new Command("q") {
        public boolean execute() {
        	msgConsole.postMessage("Annonymous inner class for \"q\" command.");
        	return true;
        }
        public String getShortDescription() {
    		return "test anonymous command class";
    	}});

        // debug commands
        cmdRecognizer.addCommand(new GoEastCommand("6", this));
        cmdRecognizer.addCommand(new GoNorthCommand("8", this));
        cmdRecognizer.addCommand(new GoNorthEastCommand("9", this));
        cmdRecognizer.addCommand(new GoNorthWestCommand("7", this));
        cmdRecognizer.addCommand(new GoSouthCommand("2", this));
        cmdRecognizer.addCommand(new GoSouthEastCommand("3", this));
        cmdRecognizer.addCommand(new GoSouthWestCommand("1", this));
        cmdRecognizer.addCommand(new GoWestCommand("4", this));

        cmdRecognizer.addCommand(new GenerateLevelCommand("!g", this));
        cmdRecognizer.addCommand(new GenerateLevelCommand("generate", this));
        cmdRecognizer.addCommand(new GeneratorConnectCommand("!c", this));
        cmdRecognizer.addCommand(new GeneratorConnectCommand("connect", this));
        cmdRecognizer.addCommand(new ResetMapBgCommand("!r", this));
        cmdRecognizer.addCommand(new ResetMapBgCommand("reset", this));
        cmdRecognizer.addCommand(new GeneratorStepCommand("!s", this));
        cmdRecognizer.addCommand(new GeneratorStepCommand("step", this));
        cmdRecognizer.addCommand(new GeneratorDigCommand("!d", this));
        cmdRecognizer.addCommand(new GeneratorDigCommand("dig", this));
        cmdRecognizer.addCommand(new GeneratorPostProcessCommand("!p", this));
        cmdRecognizer.addCommand(new GeneratorPostProcessCommand("post", this));
        cmdRecognizer.addCommand(new WaterDebugCommand("!w", this));
        cmdRecognizer.addCommand(new PrintCommandsCommand("help", this, cmdRecognizer));
        cmdRecognizer.addCommand(new PrintCommandsCommand("?", this, cmdRecognizer));
        cmdRecognizer.addCommand(new Command("find") {
        	public boolean execute() {
        		try
        		{
        			// TODO: do something here
        		}
        		catch (Exception e) { e.printStackTrace(); }
        		return true;
        	}

        	public String getShortDescription() {
        		return "LMB - start pos; RMB - dest pos; finds A* way from start to dest";
        	}
        });

        // normal commands ;]
        cmdRecognizer.addCommand(new MessagesHistoryCommand(":m", this, cmdRecognizer));
        cmdRecognizer.addCommand(new QuitCommand("Q", this, cmdRecognizer));
        cmdRecognizer.addCommand(new TurnCmd("n", this, playerTimelineEntity));

        // TIMELINE !
        timeline = new Timeline();
        timeline.register(timeline.new TimelineEntity(timeline, 99));
        timeline.register(timeline.new TimelineEntity(timeline, 97));
        timeline.register(timeline.new TimelineEntity(timeline, 102));
        timeline.register(timeline.new TimelineEntity(timeline, 107));
        timeline.register(timeline.new TimelineEntity(timeline, 142));
        timeline.register(playerTimelineEntity);
    }

    public void keyPressed(int key, char c) {
    	// override CR to be \n
    	if (c == 13)
    		c = '\n';
//    	System.out.println("key: " + key + " char: " + (int) c);
    	if (c != 0)
    		cmdRecognizer.recognize(c);

    	if (mover_y >= MAP_HEIGHT)
            mover_y = MAP_HEIGHT - 1;
        else if (mover_y < 0)
            mover_y = 0;

        if (mover_x >= MAP_WIDTH)
            mover_x = MAP_WIDTH - 1;
        else if (mover_x < 0)
            mover_x = 0;
    }

	public void keyReleased(int key, char c) {

	}


    Room selectedRoom = null;

    public void update(GameContainer container, int delta) throws SlickException
    {
    	timeline.runCycle();

        Input input = app.getInput();

        if (input.isKeyPressed(Input.KEY_ESCAPE))
        {
            app.exit();
        }

        if (input.isKeyPressed(Input.KEY_F1))
        {
            debug = !debug;
        }

        if (input.isKeyPressed(Input.KEY_SPACE))
        {
            BSPGenerator.dig_rooms = !BSPGenerator.dig_rooms;
        }

        int mx = input.getMouseX() / FONT_WIDTH;
        int my = ((input.getMouseY() - TRANSLATE_Y) / FONT_HEIGHT) - MESSAGE_CONSOLE_SIZE;

        if (input.isMousePressed(Input.MOUSE_RIGHT_BUTTON) && mx >= 0 && my >= 0 && mx < MAP_WIDTH && my < MAP_HEIGHT)
        {
            boolean founded = false;
            for (Room room : Map._rooms)
            {
                if (mx >= room.x && mx <= room.x + room.w)
                {
                    if (selectedRoom != null && my >= room.y && my <= room.y + room.h)
                    {
                        founded = true;
                        boolean connectionExists = selectedRoom.hasConnectionToRoom(room, new Vector<Room>());
                        System.out.println("connectionExists: " + connectionExists);
                    }
                }
            }
            if (!founded)
            {
                selectedRoom = null;
            }
        }

        if (input.isMousePressed(Input.MOUSE_LEFT_BUTTON) && mx >= 0 && my >= 0 && mx < MAP_WIDTH && my < MAP_HEIGHT)
        {
            boolean founded = false;
            for (Room room : Map._rooms)
            {
                if (mx >= room.x && mx <= room.x + room.w)
                {
                    if (my >= room.y && my <= room.y + room.h)
                    {
                        founded = true;
                        if (selectedRoom == null)
                        {
                            selectedRoom = room;
                        }
                        else
                        {
                            if (selectedRoom == room)
                            {
                                selectedRoom = null;
                            }
                            else
                            {
                                if (!room.connections.contains(selectedRoom) && !selectedRoom.connections.contains(room))
                                {
                                    room.connections.add(selectedRoom);
                                    selectedRoom.connections.add(room);
                                    System.out.println("Connection created.");
                                    selectedRoom = null;
                                }
                                else
                                {
                                    if (room.connections.contains(selectedRoom) && selectedRoom.connections.contains(room))
                                    {
                                        System.out.println("Connection already exists. Removed.");
                                        room.connections.remove(selectedRoom);
                                        selectedRoom.connections.remove(room);
                                        selectedRoom = null;
                                    }
                                    else
                                    {
                                        System.out.println("Some error. Connection is one way only");
                                        selectedRoom = null;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (!founded)
            {
                selectedRoom = null;
            }
        }

    }

    int neighbours = 0;

	public AStarNode nextNeighbour(AStarNode node) {
		return null;
	}
}
